Running principal component and k-means clustering analyses

dir.create("./results", showWarnings = F)
dir.create("./results/figures", showWarnings = F)
source("./src/GM_stats.R")

Performing GPA

  |                                                                                                  
  |                                                                                            |   0%
  |                                                                                                  
  |=======================                                                                     |  25%
  |                                                                                                  
  |==============================================                                              |  50%
  |                                                                                                  
  |=====================================================================                       |  75%
  |                                                                                                  
  |============================================================================================| 100%

Making projections... Finished!

[1] -0.9999859
[1] 0.9999956
[1] -0.9999535
[1] -0.9999967
[1] -0.999442
[1] -0.9999388
[1] -1
[1] 0.9997545
[1] -0.9996835
[1] 0.9999969

Plotting PC and k-means results

K-means clustering plot:

Kennel-club groupings and PC results:

Too few points to calculate an ellipse

Discriminant Factors between groups

Comparison of landmarks between group means

# Extracting first 5 PCs and each kennel-club grouping scheme
skulls <- cbind(SlicerMorph.repc[, 1:5], 
                "UKC" = factor(SlicerMorph.repc$UKC, ordered = T, levels = UKC_group_order), 
                "AKC" = factor(SlicerMorph.repc$AKC, ordered = T, levels = AKC_group_order),
                "bitework" = factor(SlicerMorph.repc$Bitework),
                "scentwork" = factor(SlicerMorph.repc$Nosework))
skulls <- na.omit(skulls)

# Create a paired and ordered GPA-grouping object
mixdrop<-gpa$coords[,,order(dimnames(gpa$coords)[[3]])]
mixdrop<-mixdrop[,,-96]

reforge<-geomorph.data.frame(shape = mixdrop,
                             UKC = na.omit(factor(SlicerMorph.repc$UKC, ordered = T)),
                             AKC = na.omit(factor(SlicerMorph.repc$AKC, ordered = T)),
                             bitework = na.omit(factor(SlicerMorph.repc$Bitework[-96])),
                             scentwork = na.omit(factor(SlicerMorph.repc$Nosework[-96])))

# Create a function that subsets the mean landmarks for each group and does a pairwise subtraction and squaring of those means
meanforge <- function(target, group) {
  #sub-setting the means and sending them to a new object
  new.coords<-coords.subset(target[["shape"]], group = target[[group]])
  output_means<-lapply(new.coords, mshape)
  #perform the subtraction and squaring of the pairwise combinations
  result<- combn(output_means, 2, function(x) (x[[1]]-x[[2]])^2, simplify = FALSE)
  #rename the pairs so they're readable
  names(result)<-combn(names(output_means), 2, function(n) paste(n[1], "-", n[2]), simplify = TRUE)
  return(result)
}

#now run the function for each grouping scheme. Each member of the list will be named based on which two groups are compared
meanshape.UKC<-meanforge(reforge,"UKC")
meanshape.AKC<-meanforge(reforge,"AKC")
meanshape.nose<-meanforge(reforge,"scentwork")
meanshape.bite<-meanforge(reforge,"bitework")

Reformatting data for visualization:

calc_gpa_dist <- function(dat){
  require(tidyr)
  n <- length(names(dat))
  results <- matrix(NA, nrow = nrow(dat[[1]]), ncol = n)
  results <- as.data.frame(results)
  results <- cbind(seq(1,nrow(results)), results)
  colnames(results) <- c("landmark",names(dat))
  
  for(i in names(dat)){
    results[[i]] <- sqrt(dat[[i]][,"X"]^2 + dat[[i]][,"Y"]^2 + dat[[i]][,"Z"]^2)
  }
  results_long <- pivot_longer(results, cols = -"landmark", names_to = "comparison")
  results_long$landmark <- factor(results_long$landmark)
  return(results_long)
}

meanshape.AKC[["plotting"]] <- calc_gpa_dist(meanshape.AKC)
meanshape.UKC[["plotting"]] <- calc_gpa_dist(meanshape.UKC)
meanshape.bite[["plotting"]] <- calc_gpa_dist(meanshape.bite)
meanshape.nose[["plotting"]] <- calc_gpa_dist(meanshape.nose)

meanshape.AKC[["plotting"]]$short <- ifelse(grepl("toy", meanshape.AKC[["plotting"]]$comparison, 
                                                  ignore.case = T),
                                           "toy", 
                                           ifelse(grepl("natural", meanshape.AKC[["plotting"]]$comparison,
                                                        ignore.case = T),
                                                  "natural", "other"))
meanshape.AKC[["plotting"]]$short <- factor(meanshape.AKC[["plotting"]]$short, 
                                            ordered = T, levels = c("toy", "natural", "other"))
meanshape.UKC[["plotting"]]$short <- ifelse(grepl("companion", meanshape.UKC[["plotting"]]$comparison,
                                                      ignore.case = T),
                                           "companion", 
                                           ifelse(grepl("natural", meanshape.UKC[["plotting"]]$comparison,
                                                      ignore.case = T),
                                           "natural", "other"))
meanshape.UKC[["plotting"]]$short <- factor(meanshape.UKC[["plotting"]]$short, 
                                            ordered = T, levels = c("companion", "natural", "other"))

AKC & UKC groups:

p_UKC_compare <- ggplot(meanshape.UKC[["plotting"]], aes(landmark, value, color = short)) + 
  geom_point(position = position_jitter(width = 0.1)) + 
  geom_hline(yintercept = meanshape.UKC[["cutoff"]]$mean+2*meanshape.UKC[["cutoff"]]$sd,
             lty = 2) +
  scale_color_viridis_d(name = "Comparison against") +
  ylab("DFA distance squared") +
  ggtitle("UKC group comparisons") +
  theme_bw() + 
  theme(legend.position = "inside", legend.position.inside = c(0.75,0.65),
        legend.background = element_rect(color = "gray30", fill = "white", linetype="solid",
                                         linewidth = 0.2))

p_AKC_compare <- ggplot(meanshape.AKC[["plotting"]], aes(landmark, value, color = short)) + 
  geom_point(position = position_jitter(width = 0.1)) + 
  geom_hline(yintercept = meanshape.AKC[["cutoff"]]$mean+2*meanshape.AKC[["cutoff"]]$sd,
             lty = 2) +
  scale_color_viridis_d(name = "Comparison against") +
  ylab("DFA distance squared") +
  ggtitle("AKC group comparisons") +
  theme_bw() +
    theme(legend.position = "inside", legend.position.inside = c(0.75,0.65),
        legend.background = element_rect(color = "gray30", fill = "white", linetype="solid",
                                         linewidth = 0.2))
p_AKC_compare / p_UKC_compare

Nosework and Bitework:

p_bite_compare <-  ggplot(meanshape.bite[["plotting"]], aes(landmark, value, color = comparison)) + 
  geom_point(position = position_jitter(width = 0.1)) + 
  geom_hline(yintercept = meanshape.bite[["cutoff"]]$mean+2*meanshape.bite[["cutoff"]]$sd,
             lty = 2) +
  scale_color_viridis_d(name = "Group-group\ncomparison") +
  ylab("DFA distance squared") +
  ggtitle("Bite-work group comparisons") +
  theme_bw()
p_nose_compare <- ggplot(meanshape.nose[["plotting"]], aes(landmark, value, color = comparison)) + 
  geom_point(position = position_jitter(width = 0.1)) + 
  geom_hline(yintercept = meanshape.nose[["cutoff"]]$mean+2*meanshape.nose[["cutoff"]]$sd,
             lty = 2) +
  scale_color_viridis_d(name = "Group-group\ncomparison") +
  ylab("DFA distance squared") +
  ggtitle("Scent-work group comparisons") +
  theme_bw()

p_bite_compare / p_nose_compare + plot_layout(guides = "collect")

MANOVA

man_UKC <- manova(as.matrix(skulls[,1:5])~skulls$UKC)
summary.aov(man_UKC, test="Pillai")
 Response PC.1 :
             Df  Sum Sq  Mean Sq F value    Pr(>F)    
skulls$UKC    8 0.65020 0.081275  35.372 < 2.2e-16 ***
Residuals   107 0.24585 0.002298                      
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

 Response PC.2 :
             Df   Sum Sq   Mean Sq F value    Pr(>F)    
skulls$UKC    8 0.073427 0.0091783  6.0866 1.902e-06 ***
Residuals   107 0.161352 0.0015080                      
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

 Response PC.3 :
             Df   Sum Sq    Mean Sq F value  Pr(>F)  
skulls$UKC    8 0.016265 0.00203313  2.4906 0.01612 *
Residuals   107 0.087348 0.00081633                  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

 Response PC.4 :
             Df   Sum Sq    Mean Sq F value    Pr(>F)    
skulls$UKC    8 0.024974 0.00312178  5.7231 4.625e-06 ***
Residuals   107 0.058365 0.00054547                      
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

 Response PC.5 :
             Df   Sum Sq    Mean Sq F value Pr(>F)
skulls$UKC    8 0.000574 0.00007179  0.1633 0.9951
Residuals   107 0.047029 0.00043952               
lda_UKC <- MASS::lda(skulls$UKC~as.matrix(skulls[,1:5]))
lda_UKC
Call:
lda(skulls$UKC ~ as.matrix(skulls[, 1:5]))

Prior probabilities of groups:
       NATURAL        Herding        Terrier      Companion        Gun dog       Guardian 
    0.19827586     0.23275862     0.08620690     0.12068966     0.05172414     0.09482759 
Northern Breed     Sighthound     Scenthound 
    0.02586207     0.14655172     0.04310345 

Group means:
               as.matrix(skulls[, 1:5])PC.1 as.matrix(skulls[, 1:5])PC.2 as.matrix(skulls[, 1:5])PC.3
NATURAL                         -0.11869015                 -0.019063433                   0.04817564
Herding                         -0.12101159                  0.026436494                   0.04577986
Terrier                         -0.05852371                  0.001880539                   0.04485686
Companion                        0.09983210                  0.018101105                   0.02331451
Gun dog                         -0.11168214                  0.041234328                   0.04624008
Guardian                        -0.06180271                  0.065382213                   0.06595086
Northern Breed                  -0.06497801                  0.049661060                   0.06220728
Sighthound                      -0.15480497                  0.029842740                   0.02964807
Scenthound                      -0.12915491                  0.049368307                   0.04321946
               as.matrix(skulls[, 1:5])PC.4 as.matrix(skulls[, 1:5])PC.5
NATURAL                        -0.016530397                    0.1158473
Herding                         0.013615782                    0.1190599
Terrier                         0.032376419                    0.1152870
Companion                       0.011348484                    0.1182048
Gun dog                         0.007520818                    0.1204955
Guardian                        0.003014570                    0.1207284
Northern Breed                 -0.017512289                    0.1170088
Sighthound                      0.018704155                    0.1147247
Scenthound                      0.010813419                    0.1131374

Coefficients of linear discriminants:
                                    LD1        LD2        LD3       LD4          LD5
as.matrix(skulls[, 1:5])PC.1  21.465233   2.072852   2.157478  1.232714   0.61490553
as.matrix(skulls[, 1:5])PC.2   2.130815 -20.151953  14.104686 -8.508683   1.48405135
as.matrix(skulls[, 1:5])PC.3 -13.131521   1.216403  21.217452 26.189753   4.17032936
as.matrix(skulls[, 1:5])PC.4  10.561161 -31.562092 -23.132983 17.258716  -0.02015756
as.matrix(skulls[, 1:5])PC.5   2.636558  -2.368104   6.558913  4.347214 -46.98480500

Proportion of trace:
   LD1    LD2    LD3    LD4    LD5 
0.7331 0.1664 0.0818 0.0172 0.0015 

Testing of Functional Groups

bite_yes_canine <- bite_data$canine[bite_data$Bitework == "yes"]
bite_no_canine <- bite_data$canine[bite_data$Bitework == "no"]
shapiro.test(bite_yes_canine)

    Shapiro-Wilk normality test

data:  bite_yes_canine
W = 0.95076, p-value = 0.5726
shapiro.test(bite_no_canine)

    Shapiro-Wilk normality test

data:  bite_no_canine
W = 0.97244, p-value = 0.08128
t.test(bite_yes_canine, bite_no_canine)

    Welch Two Sample t-test

data:  bite_yes_canine and bite_no_canine
t = -0.30306, df = 36.495, p-value = 0.7636
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 -9.152725  6.771939
sample estimates:
mean of x mean of y 
 57.78286  58.97325 
bite_yes_carnassial <- bite_data$carnassial[bite_data$Bitework == "yes"]
bite_no_carnassial <- bite_data$carnassial[bite_data$Bitework == "no"]
shapiro.test(bite_yes_carnassial)

    Shapiro-Wilk normality test

data:  bite_yes_carnassial
W = 0.92608, p-value = 0.2687
shapiro.test(bite_no_carnassial)

    Shapiro-Wilk normality test

data:  bite_no_carnassial
W = 0.96684, p-value = 0.03577
t.test(bite_yes_carnassial, bite_no_carnassial)

    Welch Two Sample t-test

data:  bite_yes_carnassial and bite_no_carnassial
t = -0.52911, df = 42.805, p-value = 0.5995
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 -9.417946  5.503589
sample estimates:
mean of x mean of y 
 57.12857  59.08575 
bite_natural_canine <- bite_data$canine[bite_data$Bitework == "NATURAL"]
shapiro.test(bite_natural_canine)

    Shapiro-Wilk normality test

data:  bite_natural_canine
W = 0.91337, p-value = 0.2672
bite_fox_canine <- bite_data$canine[bite_data$Bitework == "FOX"]
shapiro.test(bite_fox_canine)

    Shapiro-Wilk normality test

data:  bite_fox_canine
W = 0.82835, p-value = 0.03197
t.test(bite_yes_canine, bite_natural_canine)

    Welch Two Sample t-test

data:  bite_yes_canine and bite_natural_canine
t = -1.6371, df = 13.462, p-value = 0.1248
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 -29.241240   3.979681
sample estimates:
mean of x mean of y 
 57.78286  70.41364 
t.test(bite_yes_canine, bite_fox_canine)

    Welch Two Sample t-test

data:  bite_yes_canine and bite_fox_canine
t = -0.85342, df = 10.859, p-value = 0.4119
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 -29.77613  13.15585
sample estimates:
mean of x mean of y 
 57.78286  66.09300 

For the paper:

Figure 2:

Too few points to calculate an ellipse

Too few points to calculate an ellipse

Figure 4:

Saving 10 x 7.5 in image
LS0tCnRpdGxlOiAiR2VvbW9ycGhpYyBNb3JwaG9tZXRyaWNzIG9uIGNhbmlkIHNrdWxscyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CmtuaXRyOjpvcHRzX2tuaXQkc2V0KHJvb3QuZGlyID0gcnByb2pyb290OjpmaW5kX3JzdHVkaW9fcm9vdF9maWxlKCkpCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgZmlnLndpZHRoID0gOC41KQpvcHRpb25zKGtuaXRyLmdyYXBoaWNzLmVycm9yID0gRkFMU0UpCm9wdGlvbnMoa25pdHIua2FibGUuTkEgPSAnJykKb3B0aW9ucyhyZ2wudXNlTlVMTD1UUlVFKSAjIE5vdGU6IE9wZW5HTCBpcyBkZXByZWNpYXRlZCBvbiBNYWNzLCBhbmQgdGhpcyB3aWxsIGNhdXNlIGFuIGVycm9yIHdoZW4gbG9hZGluZyB0aGUgTW9ycGhvIHBhY2thZ2UuIFNldHRpbmcgdGhpcyBvcHRpb24gcmVtb3ZlcyB0aGUgZXJyb3IuIAoKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoamFuaXRvcikKbGlicmFyeShndG9vbHMpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShwYXRjaHdvcmspCmxpYnJhcnkoa2FibGVFeHRyYSkKbGlicmFyeShnZ3JlcGVsKQpsaWJyYXJ5KGdlb21vcnBoKQojIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YignU2xpY2VyTW9ycGgvU2xpY2VyTW9ycGhSJykKbGlicmFyeShTbGljZXJNb3JwaFIpCiMgZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJsaW5kc2F5d2FsZHJvcC9tdW5jaGNvbG9ycyIpCmxpYnJhcnkobXVuY2hjb2xvcnMpCiMgZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJ6YXJxdW9uNDJiL01vcnBobyIpCiNsaWJyYXJ5KE1vcnBobykKYGBgCgoKIyMgUnVubmluZyBwcmluY2lwYWwgY29tcG9uZW50IGFuZCBrLW1lYW5zIGNsdXN0ZXJpbmcgYW5hbHlzZXMKCmBgYHtyfQpkaXIuY3JlYXRlKCIuL3Jlc3VsdHMiLCBzaG93V2FybmluZ3MgPSBGKQpkaXIuY3JlYXRlKCIuL3Jlc3VsdHMvZmlndXJlcyIsIHNob3dXYXJuaW5ncyA9IEYpCnNvdXJjZSgiLi9zcmMvR01fc3RhdHMuUiIpCmBgYAoKIyMgUGxvdHRpbmcgUEMgYW5kIGstbWVhbnMgcmVzdWx0cwoKCmBgYHtyIHBsb3R0aW5nLXNldHVwLCBpbmNsdWRlPUZBTFNFfQojIFNldHRpbmcgVUtDIGdyb3VwIG9yZGVyClVLQ19ncm91cF9vcmRlciA8LSBjKCJOQVRVUkFMIiwgIkhlcmRpbmciLCAiVGVycmllciIsICJDb21wYW5pb24iLCAiR3VuIGRvZyIsIAogICAgICAgICAgICAgICAgICAgICAiR3VhcmRpYW4iLCAiTm9ydGhlcm4gQnJlZWQiLCAiU2lnaHRob3VuZCIsICJTY2VudGhvdW5kIikKQUtDX2dyb3VwX29yZGVyIDwtIGMoIk5BVFVSQUwiLCAiSGVyZGluZyIsICJUZXJyaWVyIiwgIlRveSIsICJTcG9ydGluZyIsIAogICAgICAgICAgICAgICAgICAgICAiV29ya2luZyIsICJOb24tc3BvcnRpbmciLCAiSG91bmQiKQoKIyBncm91cGluZyBwbG90cyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgRmlyc3QgYWRkZWQgeW91ciBvcmRlcmVkIGNsYXNzaWZpZXJzIG9udG8gdGhlIFBDQSBkYXRhClNsaWNlck1vcnBoLnJlcGMkVUtDIDwtIGNsc2ZfcmVvcmQkVUtDLmJyZWVkaW5nLlN0YW5kYXJkClNsaWNlck1vcnBoLnJlcGMkQUtDIDwtIGNsc2ZfcmVvcmQkQUtDLmJyZWVkaW5nLnN0YW5kYXJkCiMgU2V0IG1peGVkIGJyZWVkIHRvIE5BIGZvciBwbG90dGluZywgdGhlcmUgaXMgb25seSBvbmUhClNsaWNlck1vcnBoLnJlcGMkVUtDW1NsaWNlck1vcnBoLnJlcGMkVUtDID09ICJNSVhFRCJdIDwtIE5BClNsaWNlck1vcnBoLnJlcGMkQUtDW1NsaWNlck1vcnBoLnJlcGMkQUtDID09ICJNSVhFRCJdIDwtIE5BCiMgU2V0dGluZyBmb3ggdG8gbmF0dXJhbCBmb3IgQUtDL1VLQyBncm91cGluZyB0byBzaW1wbGlmeSBwbG90cwpTbGljZXJNb3JwaC5yZXBjJFVLQ1tTbGljZXJNb3JwaC5yZXBjJFVLQyA9PSAiRk9YIl0gPC0gIk5BVFVSQUwiClNsaWNlck1vcnBoLnJlcGMkQUtDW1NsaWNlck1vcnBoLnJlcGMkQUtDID09ICJGT1giXSA8LSAiTkFUVVJBTCIKCiMgQ2hhbmdpbmcgWWVzIHRvIGxvd2VyY2FzZQpTbGljZXJNb3JwaC5yZXBjJE5vc2V3b3JrIDwtIGlmZWxzZShjbHNmX3Jlb3JkJE5vc2UgPT0iWWVzIiB8IGNsc2ZfcmVvcmQkTm9zZSA9PSAibm8iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG9sb3dlcihjbHNmX3Jlb3JkJE5vc2UpLCBjbHNmX3Jlb3JkJE5vc2UpCiMgQ2hhbmdpbmcgTm9zZXdvcmsgdG8gb3JkZXJlZCBmYWN0b3IKU2xpY2VyTW9ycGgucmVwYyROb3Nld29yayA8LSBmYWN0b3IoU2xpY2VyTW9ycGgucmVwYyROb3Nld29yaywgb3JkZXJlZCA9IFQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJ5ZXMiLCAibm8iLCAiTkFUVVJBTCIsICJGT1giKSkKIyBDaGFuZ2luZyBZZXMgdG8gbG93ZXJjYXNlIApTbGljZXJNb3JwaC5yZXBjJEJpdGV3b3JrIDwtIGlmZWxzZShjbHNmX3Jlb3JkJEJpdGV3b3JrID09IlllcyIgfCBjbHNmX3Jlb3JkJEJpdGV3b3JrID09ICJubyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvbG93ZXIoY2xzZl9yZW9yZCRCaXRld29yayksIGNsc2ZfcmVvcmQkQml0ZXdvcmspCiMgQ2hhbmdpbmcgQml0ZXdvcmsgdG8gb3JkZXJlZCBmYWN0b3IKU2xpY2VyTW9ycGgucmVwYyRCaXRld29yayA8LSBmYWN0b3IoU2xpY2VyTW9ycGgucmVwYyRCaXRld29yaywgb3JkZXJlZCA9IFQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJ5ZXMiLCAibm8iLCAiTkFUVVJBTCIsICJGT1giKSkKIyBQdXR0aW5nIGNsdXN0ZXIgbGV2ZWxzIGludG8gYSBjb2x1bW4gYXMgYSBmYWN0b3IKU2xpY2VyTW9ycGgucmVwYyRjbHVzdGVyIDwtIGZhY3RvcihrbSRjbHVzdGVyKQoKIyBOb3cgdG8gZWFzZSB2aWV3aW5nIHdlJ2xsIGZhY3RvciB0aGVtIGludG8gYnJvYWQgY2F0ZWdvcmllcwoKIyBUdXJuaW5nIFVLQyBpc250byBhbiBvcmRlcmVkIGZhY3RvciBjb2x1bW4KU2xpY2VyTW9ycGgucmVwYyRVS0Muc2hhcGVmYWN0IDwtIGZhY3RvcihTbGljZXJNb3JwaC5yZXBjJFVLQywgb3JkZXJlZCA9IFQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IFVLQ19ncm91cF9vcmRlcikKCiMjIEFLQyBuZXh0ClNsaWNlck1vcnBoLnJlcGMkQUtDLnNoYXBlZmFjdCA8LSBmYWN0b3IoU2xpY2VyTW9ycGgucmVwYyRBS0MsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IEFLQ19ncm91cF9vcmRlcikKClNsaWNlck1vcnBoLnJlcGMkQUtDLmZhY3QgPC0gZmFjdG9yKFNsaWNlck1vcnBoLnJlcGMkQUtDKQoKIyMgU2V0dGluZyB1cCBhIGNvbHVtbiB0aGF0IGlzIGRvbWVzdGljIChkb2dzKSBhbmQgbmF0dXJhbCAoZm94ZXMsIG90aGVycykKU2xpY2VyTW9ycGgucmVwYyRkb21lc3RpYyA8LSBmYWN0b3IoaWZlbHNlKFNsaWNlck1vcnBoLnJlcGMkVUtDID09ICJOQVRVUkFMIiB8IAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTbGljZXJNb3JwaC5yZXBjJFVLQyA9PSAiRk9YIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTmF0dXJhbCIsICJEb21lc3RpY2F0ZWQiKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9yZGVyZWQgPSBULCBsZXZlbHMgPSBjKCJEb21lc3RpY2F0ZWQiLCAiTmF0dXJhbCIpKQoKYGBgCgpLLW1lYW5zIGNsdXN0ZXJpbmcgcGxvdDogCgpgYGB7ciBrLW1lYW5zLCBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcuY2FwPSJLLW1lYW5zIGNsdXN0ZXJpbmcgcGxvdC4ifQojbm93IGJ1aWxkIHRoZSBwbG90CnBrbWVhbiA8LSBnZ3Bsb3QoU2xpY2VyTW9ycGgucmVwY1shaXMubmEoU2xpY2VyTW9ycGgucmVwYyRVS0MpLF0sIGFlcyhQQy4xLCBQQy4yLCBjb2xvciA9IGNsdXN0ZXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBjbHVzdGVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNoYXBlID0gZG9tZXN0aWMpKSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDIpICsKICBzdGF0X2VsbGlwc2UoZ2VvbSA9ICJwb2x5Z29uIiwgYWVzKGZpbGwgPSBjbHVzdGVyLCBncm91cCA9IGNsdXN0ZXIpLCBsZXZlbCA9IDAuOTUsIGFscGhhID0gMC4xKSArCiAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcyA9IGMoNSwgMTkpLCBuYW1lID0gIiAiKSArCiAgc2NhbGVfY29sb3JfbXVuY2goY2hvaWNlID0gIk5pZXR6c2NoZSIsIGRpc2NyZXRlID0gVFJVRSxuYW1lID0gIkNsdXN0ZXIiKSArIAogIHNjYWxlX2ZpbGxfbXVuY2goY2hvaWNlID0gIk5pZXR6c2NoZSIsIGRpc2NyZXRlID0gVFJVRSwgbmFtZSA9ICJDbHVzdGVyIikgKwogIGdlb21fdGV4dF9yZXBlbChsYWJlbCA9IFNsaWNlck1vcnBoLnJlcGMkQnJlZWRbIWlzLm5hKFNsaWNlck1vcnBoLnJlcGMkVUtDKV0sIG1heC5vdmVybGFwcyA9IDQsIHNob3cubGVnZW5kID0gRkFMU0UpKwogIHhsYWIoIlBDIDEgKFZhciA9IDUwLjIlKSIpICsgeWxhYigiUEMgMiAoVmFyID0gMTMuMyUpIikgKwogIHRoZW1lX2J3KCkKcGttZWFuCmBgYAoKS2VubmVsLWNsdWIgZ3JvdXBpbmdzIGFuZCBQQyByZXN1bHRzOgoKYGBge3Iga2MtZ3JvdXBzLCBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcuY2FwPSJLLW1lYW5zIGNsdXN0ZXJpbmcgcGxvdC4iLCBmaWcuaGVpZ2h0PTUuNSwgZmlnLndpZHRoPTEyfQojbm93IGJ1aWxkIHRoZSBwbG90CmtjX3BhbGV0dGUgPC0gbXVuY2hfcGFsZXR0ZSgiTXVyZGVyZXIiLCA4KQprY19wYWxldHRlIDwtIGMoa2NfcGFsZXR0ZSwga2NfcGFsZXR0ZVsyXSkKcFVLQyA8LSBnZ3Bsb3QoU2xpY2VyTW9ycGgucmVwY1shaXMubmEoU2xpY2VyTW9ycGgucmVwYyRVS0MpLF0sIGFlcyhQQy4xLCBQQy4yLCBjb2xvciA9IFVLQy5zaGFwZWZhY3QsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBVS0Muc2hhcGVmYWN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNoYXBlID0gVUtDLnNoYXBlZmFjdCkpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMikgKwogIHN0YXRfZWxsaXBzZShnZW9tID0gInBvbHlnb24iLCBhZXMoZ3JvdXAgPSBVS0Muc2hhcGVmYWN0KSwgbGV2ZWwgPSAwLjk1LCBhbHBoYSA9IDAuMikgKwogIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXMgPSBjKDE5LCAwLCAxLCAyLCA1LCA2LCA3LCA5LCAxMCksIG5hbWUgPSAiVUtDIEdyb3VwcyIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0ga2NfcGFsZXR0ZSwgbmFtZSA9ICJVS0MgR3JvdXBzIikgKyAKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBrY19wYWxldHRlLCBuYW1lID0gIlVLQyBHcm91cHMiKSArCiAgZ2VvbV90ZXh0X3JlcGVsKGxhYmVsID0gU2xpY2VyTW9ycGgucmVwYyRCcmVlZFshaXMubmEoU2xpY2VyTW9ycGgucmVwYyRVS0MpXSwgbWF4Lm92ZXJsYXBzID0gNCwgc2hvdy5sZWdlbmQgPSBGQUxTRSkrCiAgCiAgeGxhYigiUEMgMSAoVmFyID0gNTAuMiUpIikgKyB5bGFiKCJQQyAyIChWYXIgPSAxMy4zJSkiKSArCiAgdGhlbWVfYncoKQpwQUtDIDwtIGdncGxvdChTbGljZXJNb3JwaC5yZXBjWyFpcy5uYShTbGljZXJNb3JwaC5yZXBjJEFLQyksXSwgYWVzKFBDLjEsIFBDLjIsIGNvbG9yID0gQUtDLnNoYXBlZmFjdCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IEFLQy5zaGFwZWZhY3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hhcGUgPSBBS0Muc2hhcGVmYWN0KSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAyKSArCiAgc3RhdF9lbGxpcHNlKGdlb20gPSAicG9seWdvbiIsIGFlcyhmaWxsID0gQUtDLnNoYXBlZmFjdCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cCA9IEFLQy5zaGFwZWZhY3QpLCBsZXZlbCA9IDAuOTUsIGFscGhhID0gMC4yKSArCiAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcyA9IGMoMTksIDAsIDEsIDIsIDUsIDYsIDcsIDksIDEwKSwgbmFtZSA9ICJBS0MgR3JvdXBzIikgKwogIHNjYWxlX2NvbG9yX211bmNoKGNob2ljZSA9ICJNdXJkZXJlciIsIGRpc2NyZXRlID0gVFJVRSwgbmFtZSA9ICJBS0MgR3JvdXBzIikgKyAKICBzY2FsZV9maWxsX211bmNoKGNob2ljZSA9ICJNdXJkZXJlciIsIGRpc2NyZXRlID0gVFJVRSwgbmFtZSA9ICJBS0MgR3JvdXBzIikgKwogIGdlb21fdGV4dF9yZXBlbChsYWJlbCA9IFNsaWNlck1vcnBoLnJlcGMkQnJlZWRbIWlzLm5hKFNsaWNlck1vcnBoLnJlcGMkQUtDKV0sIG1heC5vdmVybGFwcyA9IDQsIHNob3cubGVnZW5kID0gRkFMU0UpKwogIAogIHhsYWIoIlBDIDEgKFZhciA9IDUwLjIlKSIpICsgeWxhYigiUEMgMiAoVmFyID0gMTMuMyUpIikgKwogIHRoZW1lX2J3KCkKCnBVS0MgKyBwQUtDCmBgYAoKCiMjIERpc2NyaW1pbmFudCBGYWN0b3JzIGJldHdlZW4gZ3JvdXBzCgpDb21wYXJpc29uIG9mIGxhbmRtYXJrcyBiZXR3ZWVuIGdyb3VwIG1lYW5zCgpgYGB7cn0KIyBFeHRyYWN0aW5nIGZpcnN0IDUgUENzIGFuZCBlYWNoIGtlbm5lbC1jbHViIGdyb3VwaW5nIHNjaGVtZQpza3VsbHMgPC0gY2JpbmQoU2xpY2VyTW9ycGgucmVwY1ssIDE6NV0sIAogICAgICAgICAgICAgICAgIlVLQyIgPSBmYWN0b3IoU2xpY2VyTW9ycGgucmVwYyRVS0MsIG9yZGVyZWQgPSBULCBsZXZlbHMgPSBVS0NfZ3JvdXBfb3JkZXIpLCAKICAgICAgICAgICAgICAgICJBS0MiID0gZmFjdG9yKFNsaWNlck1vcnBoLnJlcGMkQUtDLCBvcmRlcmVkID0gVCwgbGV2ZWxzID0gQUtDX2dyb3VwX29yZGVyKSwKICAgICAgICAgICAgICAgICJiaXRld29yayIgPSBmYWN0b3IoU2xpY2VyTW9ycGgucmVwYyRCaXRld29yayksCiAgICAgICAgICAgICAgICAic2NlbnR3b3JrIiA9IGZhY3RvcihTbGljZXJNb3JwaC5yZXBjJE5vc2V3b3JrKSkKc2t1bGxzIDwtIG5hLm9taXQoc2t1bGxzKQoKIyBDcmVhdGUgYSBwYWlyZWQgYW5kIG9yZGVyZWQgR1BBLWdyb3VwaW5nIG9iamVjdAptaXhkcm9wPC1ncGEkY29vcmRzWywsb3JkZXIoZGltbmFtZXMoZ3BhJGNvb3JkcylbWzNdXSldCm1peGRyb3A8LW1peGRyb3BbLCwtOTZdCgpyZWZvcmdlPC1nZW9tb3JwaC5kYXRhLmZyYW1lKHNoYXBlID0gbWl4ZHJvcCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBVS0MgPSBuYS5vbWl0KGZhY3RvcihTbGljZXJNb3JwaC5yZXBjJFVLQywgb3JkZXJlZCA9IFQpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBBS0MgPSBuYS5vbWl0KGZhY3RvcihTbGljZXJNb3JwaC5yZXBjJEFLQywgb3JkZXJlZCA9IFQpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBiaXRld29yayA9IG5hLm9taXQoZmFjdG9yKFNsaWNlck1vcnBoLnJlcGMkQml0ZXdvcmtbLTk2XSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjZW50d29yayA9IG5hLm9taXQoZmFjdG9yKFNsaWNlck1vcnBoLnJlcGMkTm9zZXdvcmtbLTk2XSkpKQoKIyBDcmVhdGUgYSBmdW5jdGlvbiB0aGF0IHN1YnNldHMgdGhlIG1lYW4gbGFuZG1hcmtzIGZvciBlYWNoIGdyb3VwIGFuZCBkb2VzIGEgcGFpcndpc2Ugc3VidHJhY3Rpb24gYW5kIHNxdWFyaW5nIG9mIHRob3NlIG1lYW5zCm1lYW5mb3JnZSA8LSBmdW5jdGlvbih0YXJnZXQsIGdyb3VwKSB7CiAgI3N1Yi1zZXR0aW5nIHRoZSBtZWFucyBhbmQgc2VuZGluZyB0aGVtIHRvIGEgbmV3IG9iamVjdAogIG5ldy5jb29yZHM8LWNvb3Jkcy5zdWJzZXQodGFyZ2V0W1sic2hhcGUiXV0sIGdyb3VwID0gdGFyZ2V0W1tncm91cF1dKQogIG91dHB1dF9tZWFuczwtbGFwcGx5KG5ldy5jb29yZHMsIG1zaGFwZSkKICAjcGVyZm9ybSB0aGUgc3VidHJhY3Rpb24gYW5kIHNxdWFyaW5nIG9mIHRoZSBwYWlyd2lzZSBjb21iaW5hdGlvbnMKICByZXN1bHQ8LSBjb21ibihvdXRwdXRfbWVhbnMsIDIsIGZ1bmN0aW9uKHgpICh4W1sxXV0teFtbMl1dKV4yLCBzaW1wbGlmeSA9IEZBTFNFKQogICNyZW5hbWUgdGhlIHBhaXJzIHNvIHRoZXkncmUgcmVhZGFibGUKICBuYW1lcyhyZXN1bHQpPC1jb21ibihuYW1lcyhvdXRwdXRfbWVhbnMpLCAyLCBmdW5jdGlvbihuKSBwYXN0ZShuWzFdLCAiLSIsIG5bMl0pLCBzaW1wbGlmeSA9IFRSVUUpCiAgcmV0dXJuKHJlc3VsdCkKfQoKI25vdyBydW4gdGhlIGZ1bmN0aW9uIGZvciBlYWNoIGdyb3VwaW5nIHNjaGVtZS4gRWFjaCBtZW1iZXIgb2YgdGhlIGxpc3Qgd2lsbCBiZSBuYW1lZCBiYXNlZCBvbiB3aGljaCB0d28gZ3JvdXBzIGFyZSBjb21wYXJlZAptZWFuc2hhcGUuVUtDPC1tZWFuZm9yZ2UocmVmb3JnZSwiVUtDIikKbWVhbnNoYXBlLkFLQzwtbWVhbmZvcmdlKHJlZm9yZ2UsIkFLQyIpCm1lYW5zaGFwZS5ub3NlPC1tZWFuZm9yZ2UocmVmb3JnZSwic2NlbnR3b3JrIikKbWVhbnNoYXBlLmJpdGU8LW1lYW5mb3JnZShyZWZvcmdlLCJiaXRld29yayIpCgpgYGAKClJlZm9ybWF0dGluZyBkYXRhIGZvciB2aXN1YWxpemF0aW9uOgoKYGBge3J9CmNhbGNfZ3BhX2Rpc3QgPC0gZnVuY3Rpb24oZGF0KXsKICByZXF1aXJlKHRpZHlyKQogIG4gPC0gbGVuZ3RoKG5hbWVzKGRhdCkpCiAgcmVzdWx0cyA8LSBtYXRyaXgoTkEsIG5yb3cgPSBucm93KGRhdFtbMV1dKSwgbmNvbCA9IG4pCiAgcmVzdWx0cyA8LSBhcy5kYXRhLmZyYW1lKHJlc3VsdHMpCiAgcmVzdWx0cyA8LSBjYmluZChzZXEoMSxucm93KHJlc3VsdHMpKSwgcmVzdWx0cykKICBjb2xuYW1lcyhyZXN1bHRzKSA8LSBjKCJsYW5kbWFyayIsbmFtZXMoZGF0KSkKICAKICBmb3IoaSBpbiBuYW1lcyhkYXQpKXsKICAgIHJlc3VsdHNbW2ldXSA8LSBzcXJ0KGRhdFtbaV1dWywiWCJdXjIgKyBkYXRbW2ldXVssIlkiXV4yICsgZGF0W1tpXV1bLCJaIl1eMikKICB9CiAgcmVzdWx0c19sb25nIDwtIHBpdm90X2xvbmdlcihyZXN1bHRzLCBjb2xzID0gLSJsYW5kbWFyayIsIG5hbWVzX3RvID0gImNvbXBhcmlzb24iKQogIHJlc3VsdHNfbG9uZyRsYW5kbWFyayA8LSBmYWN0b3IocmVzdWx0c19sb25nJGxhbmRtYXJrKQogIHJldHVybihyZXN1bHRzX2xvbmcpCn0KCm1lYW5zaGFwZS5BS0NbWyJwbG90dGluZyJdXSA8LSBjYWxjX2dwYV9kaXN0KG1lYW5zaGFwZS5BS0MpCm1lYW5zaGFwZS5VS0NbWyJwbG90dGluZyJdXSA8LSBjYWxjX2dwYV9kaXN0KG1lYW5zaGFwZS5VS0MpCm1lYW5zaGFwZS5iaXRlW1sicGxvdHRpbmciXV0gPC0gY2FsY19ncGFfZGlzdChtZWFuc2hhcGUuYml0ZSkKbWVhbnNoYXBlLmJpdGVbWyJjdXRvZmYiXV0gPC0gZGF0YS5mcmFtZSgibWVhbiIgPSBtZWFuKG1lYW5zaGFwZS5iaXRlW1sicGxvdHRpbmciXV0kdmFsdWUpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzZCIgPSBzZChtZWFuc2hhcGUuYml0ZVtbInBsb3R0aW5nIl1dJHZhbHVlKSkKbWVhbnNoYXBlLm5vc2VbWyJwbG90dGluZyJdXSA8LSBjYWxjX2dwYV9kaXN0KG1lYW5zaGFwZS5ub3NlKQptZWFuc2hhcGUubm9zZVtbImN1dG9mZiJdXSA8LSBkYXRhLmZyYW1lKCJtZWFuIiA9IG1lYW4obWVhbnNoYXBlLm5vc2VbWyJwbG90dGluZyJdXSR2YWx1ZSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNkIiA9IHNkKG1lYW5zaGFwZS5ub3NlW1sicGxvdHRpbmciXV0kdmFsdWUpKQoKbWVhbnNoYXBlLkFLQ1tbInBsb3R0aW5nIl1dJHNob3J0IDwtIGlmZWxzZShncmVwbCgidG95IiwgbWVhbnNoYXBlLkFLQ1tbInBsb3R0aW5nIl1dJGNvbXBhcmlzb24sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlnbm9yZS5jYXNlID0gVCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidG95IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JlcGwoIm5hdHVyYWwiLCBtZWFuc2hhcGUuQUtDW1sicGxvdHRpbmciXV0kY29tcGFyaXNvbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZ25vcmUuY2FzZSA9IFQpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYXR1cmFsIiwgIm90aGVyIikpCm1lYW5zaGFwZS5BS0NbWyJwbG90dGluZyJdXSRzaG9ydCA8LSBmYWN0b3IobWVhbnNoYXBlLkFLQ1tbInBsb3R0aW5nIl1dJHNob3J0LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcmRlcmVkID0gVCwgbGV2ZWxzID0gYygidG95IiwgIm5hdHVyYWwiLCAib3RoZXIiKSkKbWVhbnNoYXBlLkFLQ1tbImN1dG9mZiJdXSA8LSBkYXRhLmZyYW1lKCJtZWFuIiA9IG1lYW4obWVhbnNoYXBlLkFLQ1tbInBsb3R0aW5nIl1dJHZhbHVlKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic2QiID0gc2QobWVhbnNoYXBlLkFLQ1tbInBsb3R0aW5nIl1dJHZhbHVlKSkKbWVhbnNoYXBlLlVLQ1tbInBsb3R0aW5nIl1dJHNob3J0IDwtIGlmZWxzZShncmVwbCgiY29tcGFuaW9uIiwgbWVhbnNoYXBlLlVLQ1tbInBsb3R0aW5nIl1dJGNvbXBhcmlzb24sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlnbm9yZS5jYXNlID0gVCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiY29tcGFuaW9uIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JlcGwoIm5hdHVyYWwiLCBtZWFuc2hhcGUuVUtDW1sicGxvdHRpbmciXV0kY29tcGFyaXNvbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWdub3JlLmNhc2UgPSBUKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYXR1cmFsIiwgIm90aGVyIikpCm1lYW5zaGFwZS5VS0NbWyJwbG90dGluZyJdXSRzaG9ydCA8LSBmYWN0b3IobWVhbnNoYXBlLlVLQ1tbInBsb3R0aW5nIl1dJHNob3J0LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcmRlcmVkID0gVCwgbGV2ZWxzID0gYygiY29tcGFuaW9uIiwgIm5hdHVyYWwiLCAib3RoZXIiKSkKbWVhbnNoYXBlLlVLQ1tbImN1dG9mZiJdXSA8LSBkYXRhLmZyYW1lKCJtZWFuIiA9IG1lYW4obWVhbnNoYXBlLlVLQ1tbInBsb3R0aW5nIl1dJHZhbHVlKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic2QiID0gc2QobWVhbnNoYXBlLlVLQ1tbInBsb3R0aW5nIl1dJHZhbHVlKSkKCmBgYAoKQUtDICYgVUtDIGdyb3VwczoKYGBge3J9CnBfVUtDX2NvbXBhcmUgPC0gZ2dwbG90KG1lYW5zaGFwZS5VS0NbWyJwbG90dGluZyJdXSwgYWVzKGxhbmRtYXJrLCB2YWx1ZSwgY29sb3IgPSBzaG9ydCkpICsgCiAgZ2VvbV9wb2ludChwb3NpdGlvbiA9IHBvc2l0aW9uX2ppdHRlcih3aWR0aCA9IDAuMSkpICsgCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gbWVhbnNoYXBlLlVLQ1tbImN1dG9mZiJdXSRtZWFuKzIqbWVhbnNoYXBlLlVLQ1tbImN1dG9mZiJdXSRzZCwKICAgICAgICAgICAgIGx0eSA9IDIpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzX2QobmFtZSA9ICJDb21wYXJpc29uIGFnYWluc3QiKSArCiAgeWxhYigiREZBIGRpc3RhbmNlIHNxdWFyZWQiKSArCiAgZ2d0aXRsZSgiVUtDIGdyb3VwIGNvbXBhcmlzb25zIikgKwogIHRoZW1lX2J3KCkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiaW5zaWRlIiwgbGVnZW5kLnBvc2l0aW9uLmluc2lkZSA9IGMoMC43NSwwLjY1KSwKICAgICAgICBsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChjb2xvciA9ICJncmF5MzAiLCBmaWxsID0gIndoaXRlIiwgbGluZXR5cGU9InNvbGlkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaW5ld2lkdGggPSAwLjIpKQoKcF9BS0NfY29tcGFyZSA8LSBnZ3Bsb3QobWVhbnNoYXBlLkFLQ1tbInBsb3R0aW5nIl1dLCBhZXMobGFuZG1hcmssIHZhbHVlLCBjb2xvciA9IHNob3J0KSkgKyAKICBnZW9tX3BvaW50KHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyKHdpZHRoID0gMC4xKSkgKyAKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBtZWFuc2hhcGUuQUtDW1siY3V0b2ZmIl1dJG1lYW4rMiptZWFuc2hhcGUuQUtDW1siY3V0b2ZmIl1dJHNkLAogICAgICAgICAgICAgbHR5ID0gMikgKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfZChuYW1lID0gIkNvbXBhcmlzb24gYWdhaW5zdCIpICsKICB5bGFiKCJERkEgZGlzdGFuY2Ugc3F1YXJlZCIpICsKICBnZ3RpdGxlKCJBS0MgZ3JvdXAgY29tcGFyaXNvbnMiKSArCiAgdGhlbWVfYncoKSArCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiaW5zaWRlIiwgbGVnZW5kLnBvc2l0aW9uLmluc2lkZSA9IGMoMC43NSwwLjY1KSwKICAgICAgICBsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChjb2xvciA9ICJncmF5MzAiLCBmaWxsID0gIndoaXRlIiwgbGluZXR5cGU9InNvbGlkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaW5ld2lkdGggPSAwLjIpKQpwX0FLQ19jb21wYXJlIC8gcF9VS0NfY29tcGFyZQpgYGAKTm9zZXdvcmsgYW5kIEJpdGV3b3JrOiAKCmBgYHtyfQpwX2JpdGVfY29tcGFyZSA8LSAgZ2dwbG90KG1lYW5zaGFwZS5iaXRlW1sicGxvdHRpbmciXV0sIGFlcyhsYW5kbWFyaywgdmFsdWUsIGNvbG9yID0gY29tcGFyaXNvbikpICsgCiAgZ2VvbV9wb2ludChwb3NpdGlvbiA9IHBvc2l0aW9uX2ppdHRlcih3aWR0aCA9IDAuMSkpICsgCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gbWVhbnNoYXBlLmJpdGVbWyJjdXRvZmYiXV0kbWVhbisyKm1lYW5zaGFwZS5iaXRlW1siY3V0b2ZmIl1dJHNkLAogICAgICAgICAgICAgbHR5ID0gMikgKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfZChuYW1lID0gIkdyb3VwLWdyb3VwXG5jb21wYXJpc29uIikgKwogIHlsYWIoIkRGQSBkaXN0YW5jZSBzcXVhcmVkIikgKwogIGdndGl0bGUoIkJpdGUtd29yayBncm91cCBjb21wYXJpc29ucyIpICsKICB0aGVtZV9idygpCnBfbm9zZV9jb21wYXJlIDwtIGdncGxvdChtZWFuc2hhcGUubm9zZVtbInBsb3R0aW5nIl1dLCBhZXMobGFuZG1hcmssIHZhbHVlLCBjb2xvciA9IGNvbXBhcmlzb24pKSArIAogIGdlb21fcG9pbnQocG9zaXRpb24gPSBwb3NpdGlvbl9qaXR0ZXIod2lkdGggPSAwLjEpKSArIAogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IG1lYW5zaGFwZS5ub3NlW1siY3V0b2ZmIl1dJG1lYW4rMiptZWFuc2hhcGUubm9zZVtbImN1dG9mZiJdXSRzZCwKICAgICAgICAgICAgIGx0eSA9IDIpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzX2QobmFtZSA9ICJHcm91cC1ncm91cFxuY29tcGFyaXNvbiIpICsKICB5bGFiKCJERkEgZGlzdGFuY2Ugc3F1YXJlZCIpICsKICBnZ3RpdGxlKCJTY2VudC13b3JrIGdyb3VwIGNvbXBhcmlzb25zIikgKwogIHRoZW1lX2J3KCkKCnBfYml0ZV9jb21wYXJlIC8gcF9ub3NlX2NvbXBhcmUgKyBwbG90X2xheW91dChndWlkZXMgPSAiY29sbGVjdCIpCmBgYAoKIyMgTUFOT1ZBIAoKYGBge3J9Cm1hbl9VS0MgPC0gbWFub3ZhKGFzLm1hdHJpeChza3VsbHNbLDE6NV0pfnNrdWxscyRVS0MpCnN1bW1hcnkuYW92KG1hbl9VS0MsIHRlc3Q9IlBpbGxhaSIpCmBgYAoKYGBge3J9CmxkYV9VS0MgPC0gTUFTUzo6bGRhKHNrdWxscyRVS0N+YXMubWF0cml4KHNrdWxsc1ssMTo1XSkpCmxkYV9VS0MKYGBgCgojIyBUZXN0aW5nIG9mIEZ1bmN0aW9uYWwgR3JvdXBzCgpgYGB7ciBmeG4tZ3JvdXBpbmdzLCBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcuaGVpZ2h0PTMuNSwgZmlnLmNhcD0iQml0ZXdvcmsgYW5kIHNjZW50IHdvcmsuIn0KZnhuX2dyb3VwX3BhbGV0dGUgPC0gbXVuY2hfcGFsZXR0ZSgiTXVyZGVyZXIiLDgpW2MoMTozLDUpXQpwYml0ZSA8LSBnZ3Bsb3QoU2xpY2VyTW9ycGgucmVwYywgYWVzKFBDLjEsIFBDLjIsIGNvbG9yID0gQml0ZXdvcmssIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBCaXRld29yaywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaGFwZSA9IEJpdGV3b3JrKSkgKyAKICBnZW9tX3BvaW50KCkgKwogIHN0YXRfZWxsaXBzZShnZW9tID0gInBvbHlnb24iLCBsZXZlbCA9IDAuOTUsIGFscGhhID0gMC4xKSArCiAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcyA9IGMoMjEsIDIzLCAwLCAyKSwgbmFtZSA9ICIgIikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBmeG5fZ3JvdXBfcGFsZXR0ZSwgbmFtZSA9ICIgIikgKyAKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBmeG5fZ3JvdXBfcGFsZXR0ZSwgbmFtZSA9ICIgIikgKwogIHhsYWIoIlBDIDEgKFZhciA9IDUwLjIlKSIpICsgCiAgeWxhYigiICIpICsKICBnZ3RpdGxlKCJTZWxlY3RlZCBmb3IgYml0ZSB3b3JrIikgKwogIHRoZW1lX2J3KCkKcHNjZW50IDwtIGdncGxvdChTbGljZXJNb3JwaC5yZXBjLCBhZXMoUEMuMSwgUEMuMiwgY29sb3IgPSBOb3Nld29yaywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IE5vc2V3b3JrLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNoYXBlID0gTm9zZXdvcmspKSArIAogIGdlb21fcG9pbnQoKSArCiAgc3RhdF9lbGxpcHNlKGdlb20gPSAicG9seWdvbiIsIGxldmVsID0gMC45NSwgYWxwaGEgPSAwLjEpICsKICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzID0gYygyMSwgMjMsIDAsIDIpLCBuYW1lID0gIiAiKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGZ4bl9ncm91cF9wYWxldHRlLCBuYW1lID0gIiAiKSArIAogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGZ4bl9ncm91cF9wYWxldHRlLCBuYW1lID0gIiAiKSArCiAgeGxhYigiUEMgMSAoVmFyID0gNTAuMiUpIikgKyAKICB5bGFiKCJQQyAyIChWYXIgPSAxMy4zJSkiKSArCiAgZ2d0aXRsZSgiU2VsZWN0ZWQgZm9yIHNjZW50IHdvcmsiKSArCiAgdGhlbWVfYncoKQpwc2NlbnQgKyBwYml0ZSArIHBsb3RfbGF5b3V0KGd1aWRlcyA9ICJjb2xsZWN0IikKCmBgYAoKYGBge3IgYml0ZS1mb3JjZSwgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KYml0ZV9kYXRhIDwtIGRhdGEuZnJhbWUoIkJyZWVkIiA9IFNsaWNlck1vcnBoLnJlcGMkQnJlZWQsIAogICAgICAgICAgICAgICAgICAgICAgICAiQml0ZXdvcmsiID0gU2xpY2VyTW9ycGgucmVwYyRCaXRld29yaywgCiAgICAgICAgICAgICAgICAgICAgICAgICJEb21lc3RpYyIgPSBTbGljZXJNb3JwaC5yZXBjJGRvbWVzdGljLCAKICAgICAgICAgICAgICAgICAgICAgICAgIlVLQyIgPSBTbGljZXJNb3JwaC5yZXBjJFVLQywgCiAgICAgICAgICAgICAgICAgICAgICAgICJBS0MiID0gU2xpY2VyTW9ycGgucmVwYyRBS0MsCiAgICAgICAgICAgICAgICAgICAgICAgICJjYW5pbmUiID0gU2xpY2VyTW9ycGgucmVwYyRiZnFfY2FuaW5lLCAKICAgICAgICAgICAgICAgICAgICAgICAgImNhcm5hc3NpYWwiID0gU2xpY2VyTW9ycGgucmVwYyRiZnFfY2FybmFzc2lhbCkKYml0ZV9kYXRhX2xvbmcgPC0gcGl2b3RfbG9uZ2VyKGJpdGVfZGF0YSwgY29scyA9IGMoImNhbmluZSIsImNhcm5hc3NpYWwiKSkKcGZvcmNlIDwtIGdncGxvdChiaXRlX2RhdGFfbG9uZywgYWVzKEJpdGV3b3JrLCB2YWx1ZSwgZmlsbCA9IG5hbWUpKSArIAogIGdlb21fYm94cGxvdChhbHBoYSA9IDAuNiwgb3V0bGllci5zaGFwZSA9IE5BKSArIAogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjUsIHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyZG9kZ2Uoaml0dGVyLndpZHRoID0gMC4yKSwgc2hhcGUgPSAyMSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IG11bmNoX3BhbGV0dGUoIlllbGxvd0xvZyIsMiksIG5hbWUgPSAiICIpICsKICB5bGFiKCJCaXRlLWZvcmNlIHF1b3RpZW50IikgKyB4bGFiKCJTZWxlY3RlZCBmb3IgYml0ZSB3b3JrIikgKwogIHRoZW1lX2J3KCkKcGZvcmNlCmBgYAoKYGBge3IgYml0ZS1mb3JjZS1zdGF0c30KYml0ZV95ZXNfY2FuaW5lIDwtIGJpdGVfZGF0YSRjYW5pbmVbYml0ZV9kYXRhJEJpdGV3b3JrID09ICJ5ZXMiXQpiaXRlX25vX2NhbmluZSA8LSBiaXRlX2RhdGEkY2FuaW5lW2JpdGVfZGF0YSRCaXRld29yayA9PSAibm8iXQpzaGFwaXJvLnRlc3QoYml0ZV95ZXNfY2FuaW5lKQpzaGFwaXJvLnRlc3QoYml0ZV9ub19jYW5pbmUpCnQudGVzdChiaXRlX3llc19jYW5pbmUsIGJpdGVfbm9fY2FuaW5lKQoKYml0ZV95ZXNfY2FybmFzc2lhbCA8LSBiaXRlX2RhdGEkY2FybmFzc2lhbFtiaXRlX2RhdGEkQml0ZXdvcmsgPT0gInllcyJdCmJpdGVfbm9fY2FybmFzc2lhbCA8LSBiaXRlX2RhdGEkY2FybmFzc2lhbFtiaXRlX2RhdGEkQml0ZXdvcmsgPT0gIm5vIl0Kc2hhcGlyby50ZXN0KGJpdGVfeWVzX2Nhcm5hc3NpYWwpCnNoYXBpcm8udGVzdChiaXRlX25vX2Nhcm5hc3NpYWwpCnQudGVzdChiaXRlX3llc19jYXJuYXNzaWFsLCBiaXRlX25vX2Nhcm5hc3NpYWwpCgpiaXRlX25hdHVyYWxfY2FuaW5lIDwtIGJpdGVfZGF0YSRjYW5pbmVbYml0ZV9kYXRhJEJpdGV3b3JrID09ICJOQVRVUkFMIl0Kc2hhcGlyby50ZXN0KGJpdGVfbmF0dXJhbF9jYW5pbmUpCmJpdGVfZm94X2NhbmluZSA8LSBiaXRlX2RhdGEkY2FuaW5lW2JpdGVfZGF0YSRCaXRld29yayA9PSAiRk9YIl0Kc2hhcGlyby50ZXN0KGJpdGVfZm94X2NhbmluZSkKCnQudGVzdChiaXRlX3llc19jYW5pbmUsIGJpdGVfbmF0dXJhbF9jYW5pbmUpCnQudGVzdChiaXRlX3llc19jYW5pbmUsIGJpdGVfZm94X2NhbmluZSkKYGBgCgojIyBGb3IgdGhlIHBhcGVyOiAKCkZpZ3VyZSAyOiAKCmBgYHtyIGZpZy0yLCBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcuY2FwPSJBLiBLLW1lYW5zIGNsdXN0ZXJpbmcgcGxvdC4gQi4gQUtDIGdyb3VwaW5ncy4gQy4gVUtDIGdyb3VwaW5ncy4iLCBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9MTB9CmRlc2lnbiA8LSAKICAiQUFBQUFBQUEjCiAgIEJCQkJDQ0NDIwogICBEREREREREREQiCnBrbWVhbiArIHBBS0MgKyBwVUtDICsKICAocF9BS0NfY29tcGFyZSArIHBfVUtDX2NvbXBhcmUpICsgCiAgcGxvdF9sYXlvdXQoZGVzaWduID0gZGVzaWduKSArCiAgcGxvdF9hbm5vdGF0aW9uKHRhZ19sZXZlbHMgPSAiQSIpCmdnc2F2ZSgiLi9yZXN1bHRzL2ZpZ3VyZXMvZmlndXJlMi5wZGYiLCBoZWlnaHQgPSAxMiwgd2lkdGggPSAxMCkKYGBgCgpgYGB7ciBmaWctMywgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLmNhcD0iQS4gSy1tZWFucyBjbHVzdGVyaW5nIHBsb3QuIEIuIEFLQyBncm91cGluZ3MuIEMuIFVLQyBncm91cGluZ3MuIiwgZmlnLmhlaWdodD03LjUsIGZpZy53aWR0aD0xMn0KIyAocF9DVkFfQUtDICsgcF9DVkFfVUtDKSArIAojICBwbG90X2Fubm90YXRpb24odGFnX2xldmVscyA9ICJBIikgKyAKIyAgcGxvdF9sYXlvdXQoZ3VpZGVzID0gImNvbGxlY3QiKSAmIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQojZ2dzYXZlKCIuL3Jlc3VsdHMvZmlndXJlcy9maWd1cmUzLnBkZiIpCmBgYAoKRmlndXJlIDQ6IAoKYGBge3IgZmlnLTQsIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy5jYXA9IkstbWVhbnMgY2x1c3RlcmluZyBwbG90LiIsIGZpZy5oZWlnaHQ9Ny41LCBmaWcud2lkdGg9MTB9Cihwc2NlbnQgKyBwYml0ZSArIHBsb3RfbGF5b3V0KGd1aWRlcyA9ICJjb2xsZWN0IikpIC8gcGZvcmNlICArIHBsb3RfYW5ub3RhdGlvbih0YWdfbGV2ZWxzID0gIkEiKQpnZ3NhdmUoIi4vcmVzdWx0cy9maWd1cmVzL2ZpZ3VyZTQucGRmIikKYGBgCgoKCgo=